#include "mod_emeter.h"

#include "usart.h"
#include "utility.h"
#include "platform.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define ELEC_DEBUG 0

#define ELEC_METER_NUM (2u)  //按2个电表设计，实际使用几个看配置文件gunnum个数
#define E_ELECMETER_TRANS_MAX (20u)
#define ELEC_VALUE_LEN (12u)
#define MAXCMD_SIZE (20u)

#define EM_CMD_TOTAL_POWER (0u)
#define EM_CMD_VOLTAGE     (1u)
#define EM_CMD_CURRENT     (2u)
#define EM_CMD_END         (3u)

enum ALARM_STATE{
	EM_ALARM_OFF = 0u,
	EM_ALARM_ON
};

enum EmeterMode{
    EM_MODE_ELEC = 0, //电表
    EM_MODE_ELECMODE, //模块
    EM_MODE_MAX,
};

struct ElecMeterSendInfo{
    uint8_t cmd[MAXCMD_SIZE];
    uint8_t pointPos; /*倍数，乘以此倍数后数值扩大100*/
} ;

static const struct ElecMeterSendInfo g_send_cmd_mode[EM_CMD_END] ={
    {{0xFEU , 0xFEU , 0xFEU , 0xFEU , 0x68U , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0x68U , 0x11U , 0x04U , 0x33U , 0x33U , 0x34U , 0x33U , 0xAEU , 0x16U}, 1U},   //total power
    {{0xFEU , 0xFEU , 0xFEU , 0xFEU , 0x68U , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0x68U , 0x11U , 0x04U , 0x33U , 0x34U , 0x34U , 0x35U , 0xB1U , 0x16U}, 10U},  //voltage
    {{0xFEU , 0xFEU , 0xFEU , 0xFEU , 0x68U , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0x68U , 0x11U , 0x04U , 0x33U , 0x34U , 0x35U , 0x35U , 0xB2U , 0x16U}, 10U},  //current
};

static const struct ElecMeterSendInfo g_send_cmd_elec[EM_CMD_END] = {
    {{0xFEU , 0xFEU , 0xFEU , 0xFEU , 0x68U , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0x68U , 0x11U , 0x04U , 0x33U , 0x33U , 0x33U , 0x33U , 0xADU , 0x16U}, 1U},  /* total power */
    {{0xFEU , 0xFEU , 0xFEU , 0xFEU , 0x68U , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0x68U , 0x11U , 0x04U , 0x33U , 0x32U , 0x34U , 0x35U , 0xB1U , 0x16U}, 10U}, /* voltage */
    {{0xFEU , 0xFEU , 0xFEU , 0xFEU , 0x68U , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0xAAU , 0x68U , 0x11U , 0x04U , 0x33U , 0x32U , 0x35U , 0x35U , 0xB2U , 0x16U}, 10U}, /* current */
};

struct ElecParseValue {
    uint8_t meterid[7];
    uint8_t val[ELEC_VALUE_LEN];
    uint8_t len;
};

struct ElecParse {
    uint8_t state;
    uint8_t cnt;
    uint8_t sum;
    struct ElecParseValue val;
};

struct ElecValue{
    uint8_t meterid[6];
    uint32_t vol; 
    uint32_t currt;
    uint32_t kwh;
};

/*单个电表对象*/
struct ElecMeterItem {
    uint8_t send_cmd;
    uint8_t keep_live;
    enum ALARM_STATE alarm_state;
    struct ElecValue value;
};

struct ElecMeter {
    uint8_t meter_num;
    uint8_t meter_mode; //0 电表，1 电表模块
    uint8_t send_meter_id;
    struct ElecMeterItem item[ELEC_METER_NUM];
    struct ElecParse ep;  /* 解析用 */
};

static struct ElecMeter emeter;

/******************************************************************/
/*涉及到对外的接口，暂时放在这里*/

#include "data.h"

/*以下接口函数，移植需要更改*/

static int32_t emeter_serial_init(int32_t baud){
    /* 注意: 电表串口为偶校验 */
    usart2_init(baud);
    return 0;
}
static int32_t emeter_send_cmd(const uint8_t *data, uint32_t len){
    usart2_send(data, len);
    return 0;
}
/*******************************************************************/

static uint8_t get_elec_meter_crc(const uint8_t *buf, uint32_t len){
    uint32_t i;
    uint32_t sum = 0U;
    for(i=0U; i<len; i++){
        sum += (uint32_t)(buf[i]);
    }
    return (uint8_t)sum;
} 

static void ElecItemSend(struct ElecMeterItem *item){
    uint8_t send_buf[32];
    uint8_t crc;

    memset(send_buf, 0, sizeof(send_buf));
    if (emeter.meter_mode == EM_MODE_ELEC){
        memcpy(send_buf, g_send_cmd_elec[item->send_cmd].cmd, sizeof(g_send_cmd_elec[item->send_cmd].cmd));

    }else{
        memcpy(send_buf, g_send_cmd_mode[item->send_cmd].cmd, sizeof(g_send_cmd_mode[item->send_cmd].cmd));
    }
    item->send_cmd = (item->send_cmd + 1u) % EM_CMD_END;	
    if (emeter.meter_num > 1){
        memcpy(&send_buf[5], item->value.meterid, sizeof(item->value.meterid));
    }
    crc = get_elec_meter_crc(&send_buf[4], 14u);
    send_buf[18] = crc;
    
    #if ELEC_DEBUG
    for(int i=0; i< 20;i++){
        printf("%02x ", send_buf[i]);
    }
    printf("\r\n");
    #endif
    
	emeter_send_cmd(send_buf, 20u);

	item->keep_live++;    
}

static void ElecSend(struct ElecMeter *em) {
    uint8_t num =  em->send_meter_id % em->meter_num; /*  0 or 1 */
    ElecItemSend(&em->item[num]);
    em->send_meter_id++;
}

static uint8_t contain(const char_t *buf) {
	uint8_t i;
    const struct ElecMeterSendInfo *info = NULL;
    if (emeter.meter_mode == EM_MODE_ELEC){
        info = g_send_cmd_elec;
    }else{
        info = g_send_cmd_mode;
    }

    for(i = 0u; i < EM_CMD_END; i++) {
        if (memcmp(buf, &info[i].cmd[14u], 4u) == 0){
            break;
        }
    }
    return i;
}

static uint32_t emeter_get_pos(const struct ElecMeter *em, const uint8_t *meterid){
    uint32_t i=0u;

    if (em->meter_num == 1u){
        i = 0u;
    }else{
        for (i=0u; i<em->meter_num; i++){
            if (memcmp(em->item[i].value.meterid, meterid, 6u) == 0){
                break;
            }
        }    
    }
    return i;
}

static void SetElecPara(struct ElecMeter *em, const uint8_t *meterid, uint8_t cmd, uint32_t value) {
    
    #if ELEC_DEBUG
    printf("elecmeter cmd:%d val:%d\r\n", cmd, value);
    #endif
    
    uint32_t pos =  emeter_get_pos(em, meterid);
    if (pos < em->meter_num){
        switch(cmd) {
        case EM_CMD_TOTAL_POWER:
            em->item[pos].value.kwh = value;
            break;
        case EM_CMD_VOLTAGE:
            em->item[pos].value.vol = value;
            break;
        case EM_CMD_CURRENT:
            em->item[pos].value.currt = value;
            break;
        default:

            break;
        } 
        em->item[pos].keep_live = 0u;
    }
}

static uint32_t ParseElecData(uint8_t cmd, const uint8_t *data, uint32_t datalen) {
    uint8_t dealbuf[20];
    uint8_t str[20];
    uint32_t i;
    uint64_t val;
    
    memset(dealbuf, 0, sizeof(dealbuf));
    for(i = 0u; i < datalen; i++) {
        dealbuf[i] = *(data + ((datalen - i) - 1u)) - 0x33u;
    }

    memset(str, 0, sizeof(str));
    val = BCD_to_Dec(dealbuf, datalen);
    int32_t mul=0;
    if (emeter.meter_mode == EM_MODE_ELEC){
        mul = g_send_cmd_elec[cmd].pointPos;
    }else{
        mul = g_send_cmd_mode[cmd].pointPos;
    }
    return (uint32_t)(val*mul); //乘以倍数将数据扩大到小数点后两位
}

static void ParseResult(struct ElecMeter *em, const struct ElecParseValue *value) {
    char_t cmd[4];
    uint8_t data[10];
    uint8_t cmdnum;
    uint32_t val;
    
    memset(cmd, 0, sizeof(cmd));
    memset(data, 0, sizeof(data));
    
    memcpy(cmd, value->val, 4u);
    memcpy(data, &value->val[4], value->len - sizeof(cmd));

    cmdnum = contain(cmd);

    if(cmdnum != EM_CMD_END) {
        val = ParseElecData(cmdnum, data, value->len - sizeof(cmd));
        SetElecPara(em, value->meterid, cmdnum, val);
    }
}


/* recv info
fe fe fe fe 68 01 00 00 00 00 00 68 91 08 33 33 34 33 5a 34 33 33 2b 16  //power
fe fe fe fe 68 01 00 00 00 00 00 68 91 06 33 34 34 35 5c 56 ea 16        //voltage
fe fe fe fe 68 01 00 00 00 00 00 68 91 07 33 34 35 35 3a 33 33 da 16     //current
*/

static void ElecParse(struct ElecMeter *em, uint8_t val) {
    switch(em->ep.state) {
    case 0: /* first 0x68 */
        if (val == (uint8_t)0x68) {
            em->ep.state++;
            em->ep.cnt = 0u;
            em->ep.sum += val;
        }
        break;
    case 1: /* meter id */
        em->ep.val.meterid[em->ep.cnt] = val;
        em->ep.cnt++;
        em->ep.sum += val;
        if (em->ep.cnt == 6u){
            em->ep.cnt = 0u;
            em->ep.state++;
        }
        break;
    case 2: /* second 0x68 */
        em->ep.sum += val;
        if (val == (uint8_t)0x68) {
            em->ep.state++;
            em->ep.cnt = 0u;
        } else {
            memset(&em->ep, 0, sizeof(struct ElecParse));
        }

        break;
    case 3: /* 0x91 */
        em->ep.sum += val;
        em->ep.state++;
        break;
    case 4: /* len */
        em->ep.sum += val;
        if ((val < ELEC_VALUE_LEN) && (val > 4u)) {
            em->ep.val.len = val;
            em->ep.state++;
            em->ep.cnt = 0u;
        } else {
            memset(&em->ep, 0, sizeof(struct ElecParse));
        }
        break;
    case 5: /* value */
        em->ep.sum += val;
        em->ep.val.val[em->ep.cnt] = val;
        em->ep.cnt++;
        if (em->ep.cnt >= em->ep.val.len) {
            em->ep.state++;
            em->ep.cnt = 0u;
        }
        break;
    case 6: /* sum crc */
        if (em->ep.sum == val) {
            em->ep.state++;
        } else {
            memset(&em->ep, 0, sizeof(struct ElecParse));
        }
        break;
    case 7: /* tail 0x16 */
        if (val == (uint8_t)0x16) {
            ParseResult(em, &em->ep.val);
        }

        memset(&em->ep, 0, sizeof(struct ElecParse));

        break;
    default:
        memset(&em->ep, 0, sizeof(struct ElecParse));
        break;
    }
}

static void ElecMeter_Recv(struct ElecMeter *em) {
    uint8_t data;
    
    /*lint -e(716) */
    while(1){
        if (usart2_receive(&data)){
            #if ELEC_DEBUG
            printf("%x ", data);
            #endif
            ElecParse(em, data);
        }else{
            break;
        }
    }
}

static void ElecMeter_CheckAlarm(struct ElecMeter *em) {
    uint32_t i;
    for(i=0u; i<em->meter_num; i++){
        if (em->item[i].keep_live >= (uint8_t)E_ELECMETER_TRANS_MAX) {
            if (em->item[i].alarm_state== EM_ALARM_OFF) {
                em->item[i].alarm_state = EM_ALARM_ON;
            }
            em->item[i].keep_live = E_ELECMETER_TRANS_MAX;
        } else {
            if (em->item[i].alarm_state == EM_ALARM_ON) {
                em->item[i].alarm_state = EM_ALARM_OFF;
            }
        }    
    }
}

static void emeter_task(void *para){
    para = para;

    /*lint -e(716) */
	while(1){	
		ElecSend(&emeter);
		os_msleep((uint32_t)300);
		ElecMeter_Recv(&emeter);
		ElecMeter_CheckAlarm(&emeter);
	}
}


uint32_t emeter_getvalue(uint8_t id, enum EM_TYPE type) {
	uint32_t value = 0u;   
    uint8_t pos = id - 1u;
    if (pos >= ELEC_METER_NUM){
        return 0;
    }
    switch(type){
    case EM_TYPE_VOL:
        value = emeter.item[pos].value.vol;   
        break;
    case EM_TYPE_CURRT:
        value = emeter.item[pos].value.currt;
        break;
    case EM_TYPE_KWH:
        value = emeter.item[pos].value.kwh;
        break;
    default:
        
        break;
    }
   
	return value;
}

int32_t emeter_isalarm(uint8_t id) {
	return (int32_t)(emeter.item[id-1u].alarm_state == EM_ALARM_ON);
}

static void convert_meterid(const uint8_t *src, uint8_t *dst){
    dst[5] = src[0];
    dst[4] = src[1];
    dst[3] = src[2];
    dst[2] = src[3];
    dst[1] = src[4];
    dst[0] = src[5];
}

int32_t emeter_init(struct EmeterInitPara *para){
    uint8_t meteridbcd[6];
    os_pthread_t emeter_thread;
    int32_t ret = 0;

    assert(para != NULL);
    
    memset(&emeter, 0, sizeof(emeter));
    emeter.meter_num = para->num;
    assert(emeter.meter_num <= ELEC_METER_NUM);
    
    emeter.meter_mode = para->mode;
    assert(emeter.meter_mode < EM_MODE_MAX);

    if (emeter.meter_num == 2){
        if (strlen(para->left_id) != 0){
            ASC_to_BCD(meteridbcd, (uint8_t *)para->left_id, 12u);
            convert_meterid(meteridbcd, emeter.item[0].value.meterid);  
        }

        if (strlen(para->right_id) != 0){
            ASC_to_BCD(meteridbcd, (uint8_t *)para->right_id, 12u);
            convert_meterid(meteridbcd, emeter.item[1].value.meterid); 
        }
    }
    
    emeter_serial_init(para->baud);
    
    if (os_pthread_create("emeter", &emeter_thread, emeter_task, NULL,  OS_TASK_PRIO_NOMAL, OS_STACK_SIZE_MEDIUM) != 0){
		ret = -1;
	}
    
    return ret;
}

